package com.m1.tzolkin.model.wheels;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.m1.tzolkin.model.Action;
import com.m1.tzolkin.model.ActionType;
import com.m1.tzolkin.model.IAction;
import com.m1.tzolkin.model.TZResource;
import com.m1.tzolkin.model.TzolkinConstants;
import com.m1.tzolkin.model.board.ITZBoard;
import com.m1.tzolkin.model.board.ITechBoard;
import com.m1.tzolkin.model.exceptions.NoAvailableSkullException;
import com.m1.tzolkin.model.exceptions.NotAvailableActionException;
import com.m1.tzolkin.model.exceptions.NotEnoughCornException;
import com.m1.tzolkin.model.exceptions.StepMoveException;
import com.m1.tzolkin.model.players.ITZPlayer;
import com.m1.tzolkin.model.technos.ITechnology.TechLevel;
import com.m1.tzolkin.model.technos.Technologies.Agriculture;
import com.m1.tzolkin.model.technos.Technologies.ResourceExtraction;
import com.m1.tzolkin.model.technos.Technologies.Theology;
import com.m1.tzolkin.model.temples.ITemple;
import com.m1.tzolkin.model.temples.TempleEnum;

public enum WheelEnum implements TzolkinConstants {
	CHICHEN_ITZA {
		public IWheel createWheel() {
			return new AbsWheel(
					"Chichen Itza", 13, 1,
					new IAction[] {
						new ChichenAction(TempleEnum.CHAAC, 4, 0),
						new ChichenAction(TempleEnum.CHAAC, 5, 0),
						new ChichenAction(TempleEnum.CHAAC, 6, 0),
						new ChichenAction(TempleEnum.QUETZALCOATL, 7, 0),
						new ChichenAction(TempleEnum.QUETZALCOATL, 8, 0),
						new ChichenAction(TempleEnum.QUETZALCOATL, 8, 1),
						new ChichenAction(TempleEnum.KUKULCAN, 10, 0),
						new ChichenAction(TempleEnum.KUKULCAN, 11, 1),
						new ChichenAction(TempleEnum.KUKULCAN, 13, 1)
					}
			) {
				/**
				 * 
				 */
				private static final long serialVersionUID = -1407374370799925438L;
				@Override
				public boolean isForbidden() { return true; }
				@Override
				public Map<IAction, Integer> removeWorker(int i) {
					if (getWorker(i).getOwner().getWallet().getSkulls() > 0) {
						return super.removeWorker(i);
					}
					return new HashMap<IAction, Integer>();
				}
			};
		}
	},
	TIKAL {
		public IWheel createWheel() {
			return new AbsWheel("Tikal", 10, 2,
					new Action[] {
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = 175042477573069832L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setTechEvolve(1, true);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = 813101399942638085L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setBuildBuildings(1);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -1081643733274470958L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setTechEvolve(2, true);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -4886794827498455092L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setBuildBuildings(2);
								b.setBuildMonuments(1);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = 7108719042695531769L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setToPay(1);
								b.setUpDifferentTemples(2);
							}
						},
					}
			);
		}
	},
	PALENQUE {
		public IWheel createWheel() {
			return new AbsWheel(
					"Palenque", 10, 2,
					new Action[] {
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -4136615350364970424L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								int add = 3 + (b.getTechnologies(p).contains(Agriculture.NOTILE_PLUSFISH) ? 1 : 0);
								p.addCorn(add);
								b.getLog().addSentence(new String[] {
									p.getName(), "EARN", "" + add, "CORN"
								});
							}
						},
						new PalenqueAction(4, 0, true),
						new PalenqueAction(5, 2, false),
						new PalenqueAction(7, 3, false),
						new PalenqueAction(9, 4, false)
					}
			);
		}
	},
	UXMAL {
		public IWheel createWheel() {
			return new AbsWheel(
					"Uxmal", 10, 2,
					new Action[] {
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -5479990926995944532L;

							public void doAction(ITZBoard b, ITZPlayer p) throws NotEnoughCornException {
								int cost = 3;
								if (p.getCorn() >= cost) {
									p.removeCorn(cost);
									b.setUpTemples(1);
									b.getLog().addSentence(new String[] {
										p.getName(), "PAY", "" + cost, "CORN", "AND", "CLIMB", "1", "STEP", "OF", "TEMPLE"	
									});
								}
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -5855234061406849766L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setExchange(true);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = 6717055416771549085L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								if (p.getTotalPawns().size() < MAX_WORKERS) {
									p.addPawn();
									b.getLog().addSentence(new String[] {
										p.getName(), "GET", "1", "WORKER"	
									});
								}
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -5281984361210814038L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								b.setPayInCorn(true);
								b.setBuildBuildings(1);
							}
						},
						new Action() {
							/**
							 * 
							 */
							private static final long serialVersionUID = -3026373559058563422L;

							public void doAction(ITZBoard b, ITZPlayer p) {
								int cost = 1;
								if (p.getCorn() >= cost) {
									Map<IAction, Integer> act = new HashMap<IAction, Integer>();
									for (WheelEnum w : WheelEnum.values()) {
										IWheel wheel = b.getWheel(w);
										if (!wheel.isForbidden()) {
											for (IAction a : wheel.getActions()) {
												act.put(a, cost);
											}
										}
									}
									b.setActions(act);
								}
							}
						}
					}
			);			
		}
	},
	YAXCHILAN {
		public IWheel createWheel() {
			return new AbsWheel("Yaxchilan", 10, 2, new Action[] {
				new Action() {
					/**
					 * 
					 */
					private static final long serialVersionUID = 2566630547911311577L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						int add = 1 + (b.getTechnologies(p).contains(ResourceExtraction.PLUSWOOD) ? 1 : 0);
						p.getWallet().addResource(TZResource.WOOD, add);
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", "" + add, "WOOD"
						});
					}
				},
				new Action() {
					/**
					 * 
					 */
					private static final long serialVersionUID = 1058622075016443241L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						int addrock = 1 + (b.getTechnologies(p).contains(ResourceExtraction.PLUSROCK) ? 1 : 0);
						p.getWallet().addResource(TZResource.STONE, addrock);
						p.addCorn(1);
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", "" + addrock, "ROCK", "AND", "1", "CORN"
						});
					}
				},
				new Action() {
					/**
					 * 
					 */
					private static final long serialVersionUID = 565625847430093872L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						int addgold = 1 + (b.getTechnologies(p).contains(ResourceExtraction.PLUSGOLD) ? 1 : 0);
						p.getWallet().addResource(TZResource.GOLD, addgold);
						p.addCorn(2);
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", "" + addgold, "GOLD", "AND", "2", "CORN"
						});
					}
				},
				new Action() {
					/**
					 * 
					 */
					private static final long serialVersionUID = 1730327977180517319L;

					public void doAction(ITZBoard b, ITZPlayer p) throws NoAvailableSkullException {
						int addskulls = 0;
						b.getBank().takeSkull();
						p.getWallet().addSkull();
						addskulls++;
						if (b.getTechnologies(p).contains(Theology.PLUSSKULL)) {
							try {
								b.getBank().takeSkull();
								p.getWallet().addSkull();
								addskulls++;
							} catch (NoAvailableSkullException e) {
								// Rien
							}
						}
						b.getLog().addSentence(new String[] {
							p.getName(), "GET", "" + addskulls, "SKULL" + (addskulls > 1 ? "S" : "")
						});
					}
				},
				new Action() {
					/**
					 * 
					 */
					private static final long serialVersionUID = 3978041042209830165L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						int addrock = 1 + (b.getTechnologies(p).contains(ResourceExtraction.PLUSROCK) ? 1 : 0);
						int addgold = 1 + (b.getTechnologies(p).contains(ResourceExtraction.PLUSGOLD) ? 1 : 0);
						p.getWallet().addResource(TZResource.STONE, addrock)
									.addResource(TZResource.GOLD, addgold);
						p.addCorn(2);
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", "" + addrock, "ROCK", ", ", "" + addgold, "GOLD", "AND", "2", "CORN"
						});
					}
				}
			});
		}
	};
	
	private static class ChichenAction extends Action {
		/**
		 * 
		 */
		private static final long serialVersionUID = -2562913072183119656L;
		private final ITemple temple;
		private final int victory;
		private final int resources;
		public ChichenAction(ITemple t, int victoryPoints, int resources) {
			super();
			temple = t;
			victory = victoryPoints;
			this.resources = resources;
		}
		public void doAction(ITZBoard b, ITZPlayer p) throws NotAvailableActionException, NoAvailableSkullException {
			if (!isAvailable()) {
				throw new NotAvailableActionException();
			}
			p.getWallet().removeSkull();
			p.addScore(victory);
			String[] res;
			try {
				b.upTemple(p, temple);
				res = new String[] {
					p.getName(), "GET", "" + victory, "POINT" + (victory > 1 ? "S" : ""), "AND", "CLIMB", "1", "STEP", "ON", temple.toString()
				};
			} catch (StepMoveException e) {
				res = new String[] {
					p.getName(), "GET", "" + victory, "POINT" + (victory > 1 ? "S" : "")
				};
			}
			b.setCollectResource(resources);
			setAvailable(false);
			Collection<TechLevel> techs = b.getTechnologies(p);
			if (techs.contains(Theology.RESOURCE_TO_TEMPLE)) {
				b.setToPay(1);
				b.setUpTemples(1);
			}
			b.getLog().addSentence(res);
			listeners.firePropertyChange("available", true, false);
		}
	}
	
	public static class PalenqueAction extends Action {
		/**
		 * 
		 */
		private static final long serialVersionUID = -5838944696077747792L;
		public static final int CORN_TILE = 0;
		public static final int WOOD_TILE = 1;
		private final boolean onlyCorn;
		private final Integer[] values;
		private final Integer[] takenTiles;
		final PalenqueAction thisAct = this;
		private PalenqueAction(int corn, int wood, boolean onlyCorn) {
			super();
			this.onlyCorn = onlyCorn;
			takenTiles = new Integer[]{ 0, 0 };
			values = new Integer[]{ corn, wood };
		}
		public void doAction(ITZBoard b, ITZPlayer p) {
			Map<IAction, Integer> a = new HashMap<IAction, Integer>();
			int corn = getTileNb(CORN_TILE, b);
			int wood = getTileNb(WOOD_TILE, b);
			// Check des technologies
			Collection<TechLevel> techs = b.getTechnologies(p);
			int n = 0;
			if (techs.contains(Agriculture.PLUSCORN)) n += 1;
			if (techs.contains(Agriculture.PLUSPLUSCORN)) n += 2;
			final int bonusCorn = n;
			n = (techs.contains(ResourceExtraction.PLUSWOOD)) ? 1 : 0;
			final int bonusWood = n;
			// S'il y a une tuile de ma�s visible
			if (corn > wood) {
				// Prise de la tuile de ma�s
				a.put(new Action(ActionType.GET_CORN_WITH_TILE) {
					/**
					 * 
					 */
					private static final long serialVersionUID = 1005268815635345972L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						p.addCorn(values[CORN_TILE] + bonusCorn);
						p.addCornTile();
						++takenTiles[CORN_TILE];
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", values[CORN_TILE] + bonusCorn + "", "CORN", "AND", "GET", "1", "CORN_TILE"
						});
						int cornTiles = getTileNb(CORN_TILE, b);
						thisAct.listeners.firePropertyChange("cornTile", cornTiles + 1, cornTiles);
					}
				}, 0);
			} else if (techs.contains(Agriculture.NOTILE_PLUSFISH)) {
				a.put(new Action(ActionType.GET_CORN_WITHOUT_TILE) {
					/**
					 * 
					 */
					private static final long serialVersionUID = -7674516505290613446L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						p.addCorn(values[CORN_TILE] + bonusCorn);
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", values[CORN_TILE] + bonusCorn + "", "CORN"
						});
					}
				}, 0);
			} else if (wood > 0) {
				// Br�lage de for�t et prise de la tuile de ma�s
				a.put(new Action(ActionType.BURN) {
					/**
					 * 
					 */
					private static final long serialVersionUID = -8334680016206772558L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						b.getCurrentPlayer().addCorn(values[CORN_TILE] + bonusCorn);
						++takenTiles[WOOD_TILE];
						++takenTiles[CORN_TILE];
						p.addCornTile();
						b.setDownTemples(1);
						b.getLog().addSentence(new String[] {
							p.getName(), "BURN", ", ", "EARN", values[CORN_TILE] + bonusCorn + "", "CORN", "AND", "GET", "1", "CORN_TILE"
						});
						int cornTiles = getTileNb(CORN_TILE, b);
						thisAct.listeners.firePropertyChange("cornTile", cornTiles + 1, cornTiles);
						int woodTiles = getTileNb(WOOD_TILE, b);
						thisAct.listeners.firePropertyChange("woodTile", woodTiles + 1, woodTiles);
					}
				}, 0);
			}
			if (wood > 0) {
				// Prise de la tuile de bois
				a.put(new Action(ActionType.GET_WOOD) {
					/**
					 * 
					 */
					private static final long serialVersionUID = -1875181822508850340L;

					public void doAction(ITZBoard b, ITZPlayer p) {
						p.getWallet().addResource(TZResource.WOOD, values[WOOD_TILE] + bonusWood);
						++takenTiles[WOOD_TILE];
						p.addWoodTile();
						b.getLog().addSentence(new String[] {
							p.getName(), "EARN", values[WOOD_TILE] + bonusWood + "", "WOOD", "AND", "GET", "1", "WOOD_TILE"
						});
						int woodTiles = getTileNb(WOOD_TILE, b);
						thisAct.listeners.firePropertyChange("woodTile", woodTiles + 1, woodTiles);
					}
				}, 0);
			}
			if (a.size() == 1) {
				for (IAction act : a.keySet()) {
					try {
						act.doAction(b, p);
					} catch (NoAvailableSkullException e) {
						// TODO Bloc catch généré automatiquement
						e.printStackTrace();
					} catch (NotEnoughCornException e) {
						// TODO Bloc catch généré automatiquement
						e.printStackTrace();
					} catch (NotAvailableActionException e) {
						// TODO Bloc catch généré automatiquement
						e.printStackTrace();
					}
				}
			} else {
				b.setActions(a);
			}
		}
		public int getTileNb(int tileType, ITechBoard b) {
			if (tileType == WOOD_TILE && onlyCorn) {
				return 0;
			}
			return b.getPlayers().size() - takenTiles[tileType];
		}
	}
	
	/**
	 * Fabrique une roue de ce type.
	 */
	public abstract IWheel createWheel();

}
